from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api.urlfetch import fetch

from google.appengine.api import memcache

import config
import hashlib

class Resource:
    path = ''
    status_code = ''
    headers = {}
    content = ''

ADMIN_STATUS = {
    0: 'Deleted succesfully',
    1: 'Not found',
    2: 'Error on storage layer',
    3: 'Bad hash',
    255: 'General error',
}
    
def renderAdminXml(status):
    xml = '''<?xml version="1.0" encoding="UTF-8"?>
        <result>
            <status>%d</status>
            <message>%s</message>
        </result>''' % (status, ADMIN_STATUS[status])
    return xml

class InvalidateResource(webapp.RequestHandler):
    def get(self, hash, path):
        self.response.headers['Content-Type'] = 'text/xml'
        
        # 3e48635acc1f772426d53912d05364aad77c8194
        salt = hashlib.sha1(''.join([config.SECRET_KEY, path])).hexdigest()
        
        status = 255
        if hash == salt:    # Hash is OK
            ret = memcache.delete(path)
            if ret == 2:  # Resource was deleted successfully
                status = 0
            elif ret == 1:  # Resource was not presented
                status = 1
            else:       # Some error occurred
                status = 2 
        else:   # Hash is wrong, nothing happen
            status = 3
        
        self.response.out.write(renderAdminXml(status))

class InvalidateAllResource(webapp.RequestHandler):
    def get(self, hash):
        self.response.headers['Content-Type'] = 'text/xml'
        
        # 904b489c218d334b79d1a68eb3d4930e2119bda3        
        salt = hashlib.sha1(config.SECRET_KEY).hexdigest()
        
        status = 255
        if hash == salt:    # Hash is OK
            ret = memcache.flush_all()
            if ret:  # Resource was deleted successfully
                status = 0
            else:
                status = 2 
        else:   # Hash is wrong, nothing happen
            status = 3
        
        self.response.out.write(renderAdminXml(status))

class GetResource(webapp.RequestHandler):
    def get(self, path):

        # Create path to URL
        path = ''.join([path, '?%s' % self.request.query_string if self.request.query_string else ''])      
    
        # Files outside META_URL are forbidden
        if not path.startswith(config.MEDIA_URL):
            self.redirect(''.join([config.SOURCE_URL, path]))
            return
        
        # Lookup into cache
        #res = Resource.gql("WHERE path = :path", path=path)
        res = memcache.get(path)

        #if res.count():
        if res:
            
            # We found object in cache, lets serve it...
            #res = res.get()
            #headers = pickle.loads(res.headers)
            headers = res.headers
                       
        else:
            
            # We don't have data in cache, so lets download data from source location
            content = fetch(''.join([config.SOURCE_URL, path]), follow_redirects=True) 
            
            #res = Resource(path=path)
            res = Resource()

            res.path = path
            res.status_code = content.status_code
            res.content = content.content
            headers = {}
            
            for h in ['content-type', 'expires', 'etag', 'cache-control']:
                try:
                    headers[h] =  content.headers[h]        
                except:
                    pass

            res.headers = headers #pickle.dumps(headers)
            #res.put()
            
            if res.status_code in config.STATUS_STORE:
                memcache.set(path, res)
                
        for h in headers.keys():
            self.response.headers[h] = headers[h]
            
        # Look at etag headers. If browser have actual version of file,
        # send 304 Not Modified instead file content    
        if 'if-none-match' in self.request.headers and \
                self.request.headers['if-none-match'] == headers['etag']:
            self.response.set_status(304) # Not modified
            return
        
        # Send result to browser
        self.response.set_status(res.status_code)
        self.response.out.write(res.content)
        
application = webapp.WSGIApplication([
                                      ('/_sys/invalidate/([a-z0-9]+)(/.*)', InvalidateResource),
                                      ('/_sys/invalidate-all/([a-z0-9]+)[/]?', InvalidateAllResource),
                                      ('(/.*)', GetResource),
                                      ], debug=False)

if __name__ == "__main__":
    run_wsgi_app(application)
